import { defineComponent, watch, h, onMounted, onBeforeUnmount, nextTick, ref } from 'vue';
import Tribute from 'tributejs';

export const VueTribute = defineComponent({
    name: 'VueTribute',
    props: {
        options: {
            type: Object,
            required: true
        }
    },
    setup(props, context) {
        if (typeof Tribute === 'undefined') {
            throw new Error('[vue-tribute] cannot locate tributejs.');
        }

        const root = ref();
        const el = ref();

        const attachTribute = (el, options = props.options) => {
            if (!el.value) return;

            const tribute = new Tribute(options);
            tribute.attach(el.value);
            el.value.tributeInstance = tribute;
        };

        onMounted(() => {
            el.value = root.value?.childNodes[0];

            if (!el.value) {
                throw new Error('[vue-tribute] cannot find a suitable element to attach to.');
            }

            attachTribute(el);

            el.value.addEventListener('tribute-replaced', (e) => {
                e.target?.dispatchEvent(new Event('input', { bubbles: true }));
            });
        });

        const detachTribute = (el) => {
            if (!el.value?.tributeInstance) return;

            el.value.tributeInstance.detach(el.value);
            el.value.tributeInstance = undefined;
            delete el.value.dataset.tribute;
        };

        onBeforeUnmount(() => {
            detachTribute(el);
        });

        watch(
            () => props.options,
            async (newOptions) => {
                if (el.value?.tributeInstance) {
                    await nextTick();
                    detachTribute(el);
                    await nextTick();
                    attachTribute(el, { ...newOptions });
                }
            },
            { deep: true }
        );

        return () =>
            h(
                'div',
                { class: 'v-tribute', ref: root },
                [context.slots.default ? context.slots.default()[0] : null].filter(Boolean)
            );
    }
});
